trait Base { // contains two virtual classes 
  type Exp <: Exp_Base
  trait Exp_Base { self: Exp => }
  
  type Lit <: Exp with Lit_Base
  def Lit(value: Int): Lit = {val l = Lit; l.value = value; l}
  def Lit: Lit
  trait Lit_Base { self: Lit =>
    var value: Int = _
  } 
  
  var zero: Lit = _
  
  def testLit: Exp = Lit(3) 
}
 
trait WithNeg extends Base  { 
  type Neg <: Exp with Neg_WithNeg
  def Neg(e: Exp): Neg
  trait Neg_WithNeg { self : Neg => 
    val e: Exp
  }
  
  def testNeg: Exp = Neg(testLit)
} 

trait WithEval extends Base { 
  type Exp <: Exp_Base with Exp_WithEval
  trait Exp_WithEval { self : Exp =>
    def eval: Int = 0
  }
  
  type Lit <: Exp with Lit_Base with Lit_WithEval
  trait Lit_WithEval extends Exp_WithEval { self : Lit =>  
// need `extends Exp_WithEval` because `eval` must explicitly override `super.eval`
// (without the `extends`, there is no `super.eval`)
    override def eval: Int = value
  }

  def testEval = testLit.eval
}

trait NegAndEval extends WithNeg with WithEval {
  type Neg <: Exp with Neg_WithNeg with Neg_NegAndEval
  trait Neg_NegAndEval extends Exp_WithEval { self: Neg =>
    override def eval = -e.eval
  }
  
  def testNegAndEval: Int = testNeg.eval
}

class Test(val f1: WithNeg, val f2: NegAndEval) {

  def Test: Int = {
    val bn = buildNeg(f1)
    n = buildNeg[f1.type](f1)(n)
    // n.eval // ERROR
    f2.zero = f2.Lit
    // n2 = buildNeg[f2.type](f2)(f1.zero) // ERROR
    n2 = buildNeg[f2.type](f2)(f2.zero)
    n2.eval
  }
  
  var n: f1.Exp = _
  var n2: f2.Exp = _
  
  case class buildNeg[WN <: WithNeg](ne: WN) {
    def apply(ex: ne.Exp): ne.Neg = ne.Neg(ex)
  }
  
}

class NegAndEval_Deploy extends NegAndEval {
  type Exp = Exp_Base with Exp_WithEval
  
  type Lit = Exp with Lit_Base with Lit_WithEval
  def Lit: Lit = new Exp_Base with Exp_WithEval with Lit_Base with Lit_WithEval
  
  type Neg = Exp with Neg_WithNeg with Neg_NegAndEval
  def Neg(ee: Exp): Neg = new Exp_Base with Exp_WithEval with Neg_WithNeg with Neg_NegAndEval {
                      val e = ee }
}

object main extends Application {
  println(new Test(new NegAndEval_Deploy, new NegAndEval_Deploy).Test)
}